/*************************************************
 * HISTORY:                                      *
 *	06/22/2020 pyx : Created.                    *
 *	03/04/2021 pyx : Edited.                     *
=================================================*/

#include "json/json.h"
#include "DATransformerUtil.h"
#include "DATransformerFileManager.h"
#include "DATransformerSymbolTable.h"
#include "DAType.h"

#include <string>
#include <vector>
#include <iostream>
#include <fstream>

int main(int argc, const char **argv)
{

	if (argc == 2)
	{ // DATransformer /workspace
		std::string directory = argv[1];

		FileManager *fm = new FileManager(directory);
		SymbolTable *st = new SymbolTable(fm);

		std::vector<std::string> jsonfilelist = listJsonFileName(argv[1]);

		int jn = jsonfilelist.size();
		if (jn == 0)
		{
			std::cout << "NO DAJSON FILE FOUND IN GIVEN DIRECTORY!!" << std::endl;
			exit(-1);
		}

		for (int i = 0; i < jn; i++)
		{

			std::string filepath = jsonfilelist[i];
			std::cout << "reading " << filepath << std::endl;
			std::ifstream is(filepath, std::ios::binary);

			if (!is.is_open())
			{
				std::cout << "open json file failed!@" << filepath << std::endl;
				return -1;
			}

			bool res;
			Json::Value root;
			Json::Reader reader;

			res = reader.parse(is, root);
			if (!res)
			{
				std::cout << "parseJson err. @" << filepath << std::endl;
				exit(-1);
			}
			std::cout << "ok1" << std::endl;

			int fn = root.size();
			if (fn == 0)
			{
				std::cout << "Found no file info. @" << filepath << std::endl;
				exit(-1);
			}

			Json::Value::Members files = root.getMemberNames();
			assert(files.size() == fn);

			// global sidmap: each json file has one!
			std::map<std::pair<unsigned, unsigned>, unsigned> sidMap;

			for (int fi = fn - 1; fi >= 0; fi--)
			{
				std::string file = files[fi];

				std::cout << "filename is =" << file << "\n";
				Json::Value tables = root[file];

				std::cout << "ok\n";

				unsigned fid = fm->addFile(file); // TODO:<SCRATCH-SPACE>ERROR

				/// FIXME!!!!!fid ==0 shouldn't be skipped because of macro, file content can be different
				if (fid == 0)
					fid = fm->getFID_not_add(file);

				std::cout << "fid=" << fid << "\n";

				int n = tables.size();
				std::cout << "n=" << n << "\n";

				if (n != 5 || !tables.isMember("symboltable") || !tables.isMember("globaldeftable") || !tables.isMember("globaldectable") || !tables.isMember("reftable") || !tables.isMember("symbolDependencyTable"))
				{
					std::cout << "invalid json title in file @" << filepath << std::endl;
					std::cout << file << std::endl;
					std::cout << tables.isMember("symboltable") << std::endl;
					std::cout << tables.isMember("globaldeftable") << std::endl;
					std::cout << tables.isMember("globaldectable") << std::endl;
					std::cout << tables.isMember("reftable") << std::endl;
					std::cout << tables.isMember("symbolDependencyTable") << std::endl;
					if (file[0] != '<')
						exit(1);
					continue;
				}

				// symboltable

				Json::Value symboltable = tables["symboltable"];
				int m = symboltable.size();

				std::cout << "m=" << m << "\n";

				for (int j = 0; j < m; j++)
				{
					Json::Value item = symboltable[j];

					int sid;
					std::string signature;
					SymbolType symbolType;
					std::string name;

					if (!item.isMember("sid"))
					{
						std::cout << "[sid] in symboltable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					sid = item["sid"].asInt();
					std::cout << "sid is: " << sid << std::endl;

					if (!item.isMember("signature"))
					{
						std::cout << "[signature] in symboltable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					signature = item["signature"].asString();
					std::cout << "signature is: " << signature << std::endl;

					if (!item.isMember("symboltype"))
					{
						std::cout << "[symboltype] in symboltable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					symbolType = (SymbolType)item["symboltype"].asInt();
					std::cout << "symboltype is: " << symbolType << std::endl;

					if (!item.isMember("name"))
					{
						std::cout << "[name] in symboltable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					name = item["name"].asString();
					std::cout << "name is: " << name << std::endl;

					unsigned newsid = st->getsid(signature, symbolType, name);

					std::pair<unsigned, unsigned> keys = std::make_pair(fid, sid);
					if (sidMap.find(keys) != sidMap.end())
					{
						if (sidMap[keys] != newsid)
						{
							std::cout << "INTERNAL ERROR! sid mapping duplicated!" << fid << "," << sid << "-->" << newsid << std::endl;
							exit(1);
						}
					}

					sidMap[keys] = newsid;

					// st->insertSymbol(sid, signature, symbolType, name);

				} // end symboltable

				// globaldeftable

				Json::Value globaldeftable = tables["globaldeftable"];
				std::cout << "ok\n";

				m = globaldeftable.size();

				std::cout << "m=" << m << std::endl;

				for (int j = 0; j < m; j++)
				{
					Json::Value item = globaldeftable[j];

					int sid;
					RefType refType;
					std::string location;

					if (!item.isMember("sid"))
					{
						std::cout << "[sid] in globaldeftable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					sid = item["sid"].asInt();
					std::cout << "sid is: " << sid << std::endl;

					std::pair<unsigned, unsigned> keys = std::make_pair(fid, sid);

					assert(sidMap.count(keys) == 1);
					sid = sidMap[keys];
					assert(sid > 0);
					std::cout << "newsid is: " << sid << std::endl;
					/*
										if (!item.isMember("fid"))
										{
											std::cout << "[fid] in globaldeftable doesn't exit in file @" << filepath << std::endl;
											break;
										}
										fid = item["fid"].asInt();
					*/
					std::cout << "fid is: " << fid << std::endl; // fid get from json 'filename' item

					if (!item.isMember("reftype"))
					{
						std::cout << "[reftype] in globaldeftable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					refType = (RefType)item["reftype"].asInt();
					std::cout << "reftype is: " << refType << std::endl;

					if (!item.isMember("location"))
					{
						std::cout << "[location] in globaldeftable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					location = item["location"].asString();
					std::cout << "location is: " << location << std::endl;

					st->insertDef(sid, fid, refType, location);

				} // end globaldeftable

				// globaldectable

				std::cout << "globaldectable\n";

				Json::Value globaldectable = tables["globaldectable"];

				std::cout << "ok\n";

				m = globaldectable.size();
				std::cout << "m=" << m << std::endl;

				for (int j = 0; j < m; j++)
				{
					Json::Value item = globaldectable[j];

					int sid;
					RefType refType;
					std::string location;

					if (!item.isMember("sid"))
					{
						std::cout << "[sid] in globaldectable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					sid = item["sid"].asInt();
					std::cout << "sid is: " << sid << std::endl;

					std::pair<unsigned, unsigned> keys = std::make_pair(fid, sid);

					assert(sidMap.count(keys) == 1);
					sid = sidMap[keys];
					assert(sid > 0);
					std::cout << "newsid is: " << sid << std::endl;
					/*
										if (!item.isMember("fid"))
										{
											std::cout << "[fid] in globaldectable doesn't exit in file @" << filepath << std::endl;
											break;
										}
										fid = item["fid"].asInt();
					*/
					std::cout << "fid is: " << fid << std::endl;

					if (!item.isMember("reftype"))
					{
						std::cout << "[reftype] in globaldectable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					refType = (RefType)item["reftype"].asInt();
					std::cout << "reftype is: " << refType << std::endl;

					if (!item.isMember("location"))
					{
						std::cout << "[location] in globaldectable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					location = item["location"].asString();
					std::cout << "location is: " << location << std::endl;

					st->insertDec(sid, fid, refType, location);

				} // end globaldectable

				// reftable

				Json::Value reftable = tables["reftable"];
				std::cout << "ok\n";

				m = reftable.size();

				std::cout << "m=" << m << std::endl;

				for (int j = 0; j < m; j++)
				{
					Json::Value item = reftable[j];

					int sid;
					RefType refType;
					std::string location;

					if (!item.isMember("sid"))
					{
						std::cout << "[sid] in reftable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					sid = item["sid"].asInt();
					std::cout << "sid is: " << sid << std::endl;

					std::pair<unsigned, unsigned> keys = std::make_pair(fid, sid);

					assert(sidMap.count(keys) == 1);
					sid = sidMap[keys];
					assert(sid > 0);
					std::cout << "newsid is: " << sid << std::endl;
					/*
										if (!item.isMember("fid"))
										{
											std::cout << "[fid] in reftable doesn't exit in file @" << filepath << std::endl;
											break;
										}
										fid = item["fid"].asInt();
					*/
					std::cout << "fid is: " << fid << std::endl;

					if (!item.isMember("reftype"))
					{
						std::cout << "[reftype] in reftable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					refType = (RefType)item["reftype"].asInt();
					std::cout << "reftype is: " << refType << std::endl;

					if (!item.isMember("location"))
					{
						std::cout << "[location] in reftable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					location = item["location"].asString();
					std::cout << "location is: " << location << std::endl;

					st->insertRef(sid, fid, refType, location);

				} // end globalreftable

				std::cout << "ok reftable\n";

				// symboldeptable

				Json::Value symboldeptable = tables["symbolDependencyTable"];
				m = symboldeptable.size();

				std::cout << "m=" << m << "\n";

				for (int j = 0; j < m; j++)
				{
					Json::Value item = symboldeptable[j];

					int ssid;
					int esid;
					std::string efilepath;
					SDependencyType symbolDepType;

					if (!item.isMember("ssid"))
					{
						std::cout << "[ssid] in symboldeptable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					ssid = item["ssid"].asInt();
					std::cout << "ssid is: " << ssid << std::endl;

					if (!item.isMember("esid"))
					{
						std::cout << "[esid] in symboldeptable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					esid = item["esid"].asInt();
					std::cout << "esid is: " << esid << std::endl;

					if (!item.isMember("efilepath"))
					{
						std::cout << "[efilepath] in symboldeptable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					efilepath = item["efilepath"].asString();
					std::cout << "efilepath is: " << efilepath << std::endl;

					if (!item.isMember("SDependencyType"))
					{
						std::cout << "[SDependencyType] in symboldeptable doesn't exit in file @" << filepath << std::endl;
						break;
					}
					symbolDepType = (SDependencyType)item["SDependencyType"].asInt();
					std::cout << "symboltype is: " << symbolDepType << std::endl;

					st->insertSymbolDep(ssid, fid, esid, efilepath, symbolDepType);

				} // end symboldeptable

			} // end fi

			is.close();
			std::cout << "end reading" << filepath << std::endl;

			st->mappingSymbolDep(sidMap);

		} // end json file

		//!!fm finalize first!!
		fm->finilize();
		st->finilize();

		delete fm;
		std::cout << "delete fm ok!" << std::endl;
		delete st;
		std::cout << "delete sm ok!" << std::endl;

	} // endif
	else
	{
		std::cout << "Wrong Parameter!" << std::endl;
		exit(-1);
	}

	return 0;
}
